/*
 
 TopMind jLynx "jLynx JDBC Framework"
 Copyright (c) 2004-2007. TopMind Systems Inc.
 All rights reserved.
 
 This file is part of TopMind jLynx.
 
 TopMind jLynx is free software; you can redistribute it and/or modify
 it under the terms of the License. See website for License.
 
 */
package net.sf.jlynx;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.naming.NamingException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of Relational interface. Use <code>RelationalFactory</code>
 * to obtain instance.
 * 
 * @see biz.topmind.jlynx.Relational
 * 
 * $Id: RelationalImpl.java 99 2007-06-21 02:51:13Z gritc11 $
 * 
 */
final class RelationalImpl implements Relational {

	private static Logger logger = LoggerFactory
			.getLogger(RelationalImpl.class);

	private static Map pkMap = new TreeMap();

	private static List removeProp;

	private static final long serialVersionUID = 1L;

	static {

		// TODO make this XML-configurable
		removeProp = new ArrayList();
		removeProp.add("class");
		removeProp.add("multipartRequestHandler");
		removeProp.add("servletWrapper");
		removeProp.add("entity");
		removeProp.add("bean");
		removeProp.add("validConfig");
		removeProp.add("page");
		removeProp.add("resultValueMap");
		removeProp.add("validatorResults");

	}

	private static String fixNulls(String sqlIn) {
		return StringUtils.replace(sqlIn, "'null'", "NULL");
	}

	/**
	 * 
	 * pre-condition: ResultSet next() called
	 * 
	 * @param rs
	 * @param object
	 * @return
	 * @throws Exception
	 */
	private static Object setValues(ResultSet rs, Object object)
			throws Exception {

		if (object == null)
			return null;

		try {

			int colCount = rs.getMetaData().getColumnCount();
			for (int i = 1; i <= colCount; i++) {

				String colName = rs.getMetaData().getColumnName(i);

				Object value = rs.getObject(i);

				if (object instanceof Map) {

					((Map) object).put(colName, value);

				} else {

					BeanUtils.setValue(colName, object, value);

				}

			}// end for

		} catch (SQLException e) {
			throw e;
		}
		return object;
	}

	// private member fields
	private boolean autoCommit = true;

	private Object bean;

	Connection conn;

	private ConnectionBean connBean;

	protected int dbVendor;

	private boolean domapping = false;

	private String entityName;

	private boolean initialized = false;

	private boolean keepNullsInQuery = false;

	private String keyField;

	private String[] keyFields;

	private Map mappings;

	private PreparedStatement ps;

	private ResultSet rs;

	private Statement stmt;

	private boolean validConfig = true;

	private RelationalImpl() {
	}

	RelationalImpl(String entity, ConnectionBean cb) {
		if (cb != null)
			this.connBean = cb;
		setEntity(entity);
	}

	public void commit() {
		if (conn != null) {
			try {
				conn.commit();
				this.autoCommit = true;
				release();
			} catch (SQLException e) {
				logger.error(e.getMessage());
				e.printStackTrace();
			} finally {
				this.autoCommit = true;
				conn = null;
			}
		} else
			logger.warn("Connection already closed!");
	}

	private final void connect() throws SQLException {

		if (conn == null && ConfigParser.isParsed) {

			try {
				if (connBean == null)
					conn = new TransactionManager().open();
				else
					conn = new TransactionManager().open(connBean.getName());
			} catch (NamingException e) {
				e.printStackTrace();
				logger.error(e.getMessage());
				throw new SQLException(e.getMessage());
			}
			logger.debug("conn = " + conn);

			// set auto commit for connection object
			if (conn != null)
				conn.setAutoCommit(autoCommit);

		} else
			throw new SQLException("No database connection!");

	}

	private String createFilterStmt() throws NoSuchMethodException,
			SQLException {

		// TODO handle Maps for multi-part keys

		String newKey = null;
		if (mappings != null && mappings.containsValue(keyField)) {
			// logger.debug(mappings + keyField);
			Iterator i = mappings.keySet().iterator();
			while (i.hasNext()) {
				newKey = (String) i.next();
				if (keyField.equals(mappings.get(newKey)))
					break;
				else
					newKey = null;
			}
		}

		if (newKey != null)
			keyField = newKey;

		String sql = " WHERE ";
		String delimiter = "'";

		Object obj = this.bean;

		if (keyField != null) {

			Object keyVal = null;

			try {

				if (obj instanceof Map) {
					keyVal = ((Map) obj).get(keyField);
					if (keyVal == null)
						keyVal = ((Map) obj).get(keyField.toLowerCase());
				} else {

					keyVal = BeanUtils.getValue(keyField, obj);

				}

				if (keyVal == null)
					throw new Exception();

				// fix for DB2 (numbers cannot be wrapped with apostrophe)
				if (DataTypeMappings.isNumber(keyVal))
					delimiter = "";

			} catch (Exception e) {
				throw new SQLException(
						"Primary key cannot be determined :: jlynx.xml mapping needed");
			}

			sql += fixUpdateMappings(keyField) + "=" + delimiter
					+ keyVal.toString() + delimiter;

			// logger.debug("WHERE clause :: " + sql);

			return sql;

		} else if (keyFields != null) {
			int keySize = keyFields.length;
			for (int i = 0; i < keySize; i++) {
				if (i != 0) {
					sql += " AND ";
				}
				sql += keyFields[i];

				// String keyValue = "";
				Object partKeyValue = null;

				partKeyValue = BeanUtils.getValue(keyFields[i], obj);

				// DB2 fix as above
				if (DataTypeMappings.isNumber(partKeyValue))
					delimiter = "";
				else
					delimiter = "'";

				sql += "=" + delimiter + partKeyValue.toString() + delimiter;
			}
		}
		return sql;
	}

	private String createInsertStmt() throws NoSuchMethodException {

		StringBuffer sql = new StringBuffer();

		Map props;
		String delimiter = "'";

		Object obj = bean;

		if (obj instanceof Map)
			props = (Map) obj;
		else
			props = BeanUtils.describe(obj);

		// remove useless properties from this object
		Iterator iter = removeProp.iterator();
		while (iter.hasNext())
			props.remove((String) iter.next());

		// fix to account for identity fields in SQL Server and other
		// default values
		removeNulls(props);

		Iterator i = props.keySet().iterator();
		String fld = null;
		String[] fields = new String[props.size()];
		int j = 0;
		while (i.hasNext()) {
			fld = (String) i.next();

			fields[j] = fld;
			j++;
			// logger.debug(fld);
			sql.append(fld);
			if (i.hasNext()) {
				sql.append(",");
			} else {
				// fix for configured column mappings
				String end = ",$END-OF-COLUMN65$";
				sql.append(end);
				// logger.debug(sql);
				if (domapping)
					sql = new StringBuffer(fixInsertMappings(sql.toString()));
				// logger.debug(sql);
				sql = new StringBuffer(StringUtils.replace(sql.toString(), end,
						""));
				sql.append(") VALUES (");
			}
		}

		i = props.values().iterator();
		j = 0;
		while (i.hasNext()) {

			Object val = i.next();
			// logger.debug(val);
			String oracleDate1 = "";
			String oracleDate2 = "";

			if (dbVendor == TransactionManager.ORACLE) {

				// Oracle fix for Dates
				if (java.sql.Timestamp.class.equals(BeanUtils.getType(
						fields[j], obj))) {
					// System.out.println(fld);
					oracleDate1 = "to_date(";
					oracleDate2 = ",'yyyy-mm-dd hh24:mi:ss\".999\"')";

				}
			}

			if (dbVendor == TransactionManager.MSSQL) {

				// MSSQL fix for Bits/Boolean
				Class cls = BeanUtils.getType(fields[j], obj);

				if (Boolean.class.equals(cls)) {

					Boolean valB = (Boolean) val;

					if (valB.booleanValue())
						val = "1";
					else
						val = "0";

					logger.debug("SQL bit fix :: " + cls);

				} else if (cls == boolean.class) {
					// boolean bit = ((Boolean) val).booleanValue();
					if ("true".equals(val.toString()))
						val = "1";
					else
						val = "0";

					logger.debug("SQL bit fix :: " + cls);
				}

			}

			Object field = null;

			// field = PropertyUtils.getProperty(obj, fields[j]);
			field = BeanUtils.getValue(fields[j], obj);

			if (DataTypeMappings.isNumber(field)) {
				delimiter = "";
			} else
				delimiter = "'";

			j++;

			sql.append(oracleDate1 + delimiter
					+ StringUtils.escapeQuotes(val.toString()) + delimiter
					+ oracleDate2);
			if (i.hasNext()) {
				sql.append(",");
			} else {
				sql.append(")");
			}
		}

		String result = "INSERT INTO " + entityName + " ("
				+ fixNulls(sql.toString());

		logger.debug("INSERT stmt: " + result);

		return result;
	}

	private String createSelectStmt() throws NoSuchMethodException,
			SQLException {

		String cols = fixSelectMappings();

		if (cols.equalsIgnoreCase(""))
			cols = "*";
		else
			cols = entityName + ".*" + cols;

		String sql = "SELECT " + cols + " FROM " + entityName
				+ createFilterStmt();

		logger.debug("SELECT SQL statement: " + sql);

		return sql;
	}

	private String createUpdateStmt() throws Exception {

		String delimiter = "'";

		String sql = "UPDATE " + entityName + " SET ";

		String where = createFilterStmt();

		logger.debug("WHERE clause: " + where);

		Map map = new TreeMap();

		if (bean instanceof Map) {
			map.putAll((Map) this.bean);
			logger.debug("Map " + map);
		} else
			map.putAll(BeanUtils.describe(bean));

		// remove useless properties from this object
		Iterator iter = removeProp.iterator();
		while (iter.hasNext())
			map.remove((String) iter.next());

		// remove null values from the bean
		removeNulls(map);

		Iterator i = map.keySet().iterator();
		int j = 0;
		while (i.hasNext()) {

			j++;
			String colName = (String) i.next();
			String value = null;
			boolean isMap = bean instanceof Map;
			Object o = null;
			o = map.get(colName);

			if (o != null) {
				value = StringUtils.escapeQuotes(o.toString());
				if (this.keyField != null) {
					if (colName.equalsIgnoreCase(keyField))
						value = null;
				} else if (this.keyFields != null) {
					for (int k = 0; k < keyFields.length; k++) {
						if (colName.equalsIgnoreCase(keyFields[k]))
							value = null;
					}
				} else
					throw new SQLException(
							"Primary key(s) is not set for the UPDATE query");// no
				// primary
				// key
			}

			if (value != null) {

				String oracleDate1 = "";
				String oracleDate2 = "";

				if (!isMap && dbVendor == TransactionManager.ORACLE) {

					// Oracle fix for Dates
					Class cls = BeanUtils.getType(colName, bean);
					if (java.sql.Timestamp.class.equals(cls)) {
						// System.out.println("Oracle Date");
						oracleDate1 = "to_date(";
						oracleDate2 = ",'yyyy-mm-dd hh24:mi:ss\".999\"')";

					}
				}

				if (!isMap && dbVendor == TransactionManager.MSSQL) {

					Class cls = BeanUtils.getType(colName, bean);

					// MSSQL fix for Bits/Boolean
					if (Boolean.class.equals(cls)) {
						Boolean valB = (Boolean) BeanUtils.getValue(colName,
								bean);
						if (valB.booleanValue())
							value = "1";
						else
							value = "0";
						logger.debug("SQL bit fix :: " + cls);

					} else if (cls == boolean.class) {

						if ("true".equals(value.toString()))
							value = "1";
						else
							value = "0";
						logger.debug("SQL bit fix :: " + cls);
					}
				}

				// do fix here for DB2 numeric types
				if (!isMap
						&& DataTypeMappings.isNumber(BeanUtils.getValue(
								colName, bean))) {
					delimiter = "";
				} else
					delimiter = "'";

				// logger.debug("Bean:Column pairing: " + colName + "::" +
				// fixUpdateMappings(colName));

				colName = fixUpdateMappings(colName);

				sql += colName + "=" + oracleDate1 + delimiter + value
						+ delimiter + oracleDate2 + ", ";
			}

		}
		if (j == 0) {
			return null;
		}

		sql = sql.substring(0, sql.length() - 2) + where;

		sql = fixNulls(sql);

		logger.debug("UPDATE stmt: " + sql);

		return sql;
	}

	public final boolean delete() throws SQLException {

		if (!validConfig) {
			logger.error("jLynx configuration invalid");
			throw new SQLException("jLynx configuration invalid");
		}

		if (!initialized)
			init();

		String sql = null;
		try {
			sql = "DELETE FROM " + entityName + createFilterStmt();

			logger.debug(sql);
			if (conn == null) {
				connect();
			}
			stmt = conn.createStatement();
			int result = stmt.executeUpdate(sql);

			if (result == 1) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			e.printStackTrace();
			if (e instanceof SQLException) {
				throw (SQLException) e;
			} else
				throw new SQLException(e.getMessage());
		} finally {
			logger.debug(sql);
			release();
		}
	}

	public boolean executeNamedQuery(String query, Object[] p)
			throws SQLException {

		String sql = (ConfigParser.getQuery(query) == null || ""
				.equals(ConfigParser.getQuery(query))) ? query : ConfigParser
				.getQuery(query);
		logger.debug("SQL stmt = " + sql);

		boolean result = false;
		if (conn == null)
			connect();

		ps = conn.prepareStatement(sql);
		logger.debug("conn = " + conn + ":" + autoCommit);

		setParams(p);
		result = ps.execute();
		if (this.autoCommit)
			release();
		logger.debug("Exiting");
		return result;

	}

	private List executeQuery() throws SQLException {

		logger.debug("Entering");
		List result = new ArrayList();

		if (ps != null)
			rs = ps.executeQuery();

		while (rs != null && rs.next()) {

			try {

				Object obj;
				if (bean instanceof Class)
					obj = ((Class) bean).newInstance();
				else
					obj = bean.getClass().newInstance();
				obj = setValues(rs, obj);
				result.add(obj);

			} catch (Exception e) {
				// TODO
			}
		}

		release();
		logger.debug("Exiting");
		return result;

	}

	private String fixInsertMappings(String sql) {

		if (mappings == null || mappings.isEmpty())
			return sql;

		Iterator iter = mappings.keySet().iterator();
		while (iter.hasNext()) {
			String col = (String) iter.next();
			sql = StringUtils.replace(sql, col + ",", (String) mappings
					.get(col)
					+ ",");
		}
		return sql;

	}

	private String fixSelectMappings() {

		if (mappings != null && !mappings.isEmpty()) {

			String sql = "";
			Iterator iter = mappings.keySet().iterator();
			while (iter.hasNext()) {

				String jCol = (String) iter.next();
				String dbCol = (String) mappings.get(jCol);

				if (jCol != null && dbCol != null)
					sql += "," + dbCol + " \"" + jCol + "\"";

			}
			return sql;

		} else
			return "";

	}

	private String fixUpdateMappings(String columnName) {

		if (mappings != null && mappings.containsKey(columnName))
			return (String) mappings.get(columnName);
		else
			return columnName;

	}

	// Returns the entityName.
	private final String getEntity() {
		return entityName;
	}

	public List getResultList(String query, Object[] p) throws SQLException {

		if (conn == null)
			connect();

		String sql = (ConfigParser.getQuery(query) == null || ""
				.equals(ConfigParser.getQuery(query))) ? query : ConfigParser
				.getQuery(query);

		logger.debug("SQL stmt = " + sql);

		this.ps = this.conn.prepareStatement(sql);

		setParams(p);
		List list = executeQuery();
		return list;
	}

	/**
	 * 
	 */
	public String getResultListXml(String namedQuery, Object[] params,
			String elementName) throws SQLException {
		List list = this.getResultList(namedQuery, params);
		return BeanUtils.toXml(list, elementName);
	}

	// initialize; setup primary keys
	private void init() throws SQLException {

		if (conn != null && getEntity() != null) {

			domapping = false;
			initialized = true;

			try {

				String url = conn.getMetaData().getURL();
				String pkMapKey = url + "|" + getEntity();
				List pks = (List) pkMap.get(pkMapKey);

				if (pks == null) {

					this.dbVendor = SchemaUtil.findDbVendor(conn.getMetaData());
					ResultSet r = conn.getMetaData().getPrimaryKeys(null, null,
							getEntity());

					pks = new ArrayList();
					String pk = null;
					while (r.next()) {
						pk = r.getString(4);
						pks.add(pk);
					}
					pkMap.put(pkMapKey, pks);
				}

				if (pks.size() == 1) {
					this.keyField = pks.get(0).toString();
				} else if (pks.size() > 1) {
					this.keyFields = (String[]) pks.toArray();
				} else
					logger.warn("Primary key not found for entity: "
							+ getEntity());

				setAutoCommit(conn.getAutoCommit());
				logger.debug("Primary key map... " + pkMap);
				if (!ConfigParser.isParsed)
					return;

			} catch (SQLException e) {
				throw e;
			}

		}

		// exit if bean is a Map and setEntity() has not been called yet
		if (bean == null
				|| (bean instanceof Map && getEntity().equalsIgnoreCase(
						bean.getClass().getName().toString().toLowerCase())))
			return;

		logger.debug("entity = " + getEntity() + " object class = "
				+ bean.getClass());

		if (conn == null && ConfigParser.isParsed) {
			String db;

			if (connBean == null) {

				db = ConfigParser.DEFAULT;

				connBean = (ConnectionBean) ConfigParser.getConnectionDefs()
						.get(db);

			} else {
				db = connBean.getName();
			}

			try {

				this.mappings = (Map) connBean.getMappings().get(
						this.getEntity());
				logger.debug("Column name mappings :: " + mappings);
				domapping = true;

			} catch (NullPointerException npe) {
				domapping = false;
				mappings = null;
			}

			if (keyField == null && keyFields == null) {

				Object[] keySet;

				DatabaseMetaBean dm = (DatabaseMetaBean) SchemaUtil.DATABASE_INFO
						.get(db);

				if (dm == null) {
					logger.error("Database not configured :: DB alias = " + db);
					validConfig = false;
					return;
				}

				this.dbVendor = dm.getDbVendor();

				// make sure we do below in a case-sensitive manner
				// i.e. we put tables in databasemetabean case-sensitive and we
				// fetch case-sensitive
				keySet = (Object[]) dm.getTables().get(
						getEntity().toLowerCase());

				if (keySet == null || keySet.length == 0) {

					if (!(bean instanceof Map))
						logger
								.error("entity named <<"
										+ getEntity()
										+ ">> does not exist or does not have a primary key");

					validConfig = false;

				} else if (keySet.length == 1) {

					// single pri key
					keyField = (String) keySet[0];

					logger.debug("pri key = " + keyField);

					validConfig = true;

				} else {

					// composite pri key
					keyFields = new String[keySet.length];
					for (int i = 0; i < keySet.length; i++)
						keyFields[i] = (String) keySet[i];

					validConfig = true;

				}
			}
		}
		initialized = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see biz.topmind.jlynx.Relational#insert()
	 */
	public final int insert() throws SQLException {

		if (!validConfig && conn == null && ConfigParser.isParsed) {
			logger.error("jlynx.xml configuration invalid");
			throw new SQLException("jlynx.xml configuration invalid");
		}

		if (!initialized)
			init();

		int result;
		try {

			if (conn == null)
				connect();

			String sql = createInsertStmt();
			stmt = conn.createStatement();
			logger.debug(sql);
			result = stmt.executeUpdate(sql);
			if (dbVendor == TransactionManager.MSSQL && result == 1) {
				try {
					int ident = 1;
					String identitySql = "SELECT SCOPE_IDENTITY()";

					rs = stmt.executeQuery(identitySql);
					if (rs.next())
						ident = rs.getInt(1);

					logger
							.debug(dbVendor + " : " + identitySql + " = "
									+ ident);

					result = ident;
				} catch (Exception e) {
					result = 1;
				}
			} else if (dbVendor == TransactionManager.MYSQL && result == 1) {

				// use mysql last_insert_id() to return the auto_increment value
				// if it returns 0, return 1 instead

				String ident = "";

				rs = stmt.executeQuery("select LAST_INSERT_ID() as ident");
				if (rs.next())
					ident = rs.getString(1);

				logger.debug("mysql LAST_INSERT_ID() = " + ident);

				try {
					result = new Integer(ident).intValue();
					if (result == 0)
						result = 1;
				} catch (NumberFormatException e) {
					result = 1;
				}
			}

		} catch (Exception e) {
			logger.error(e.getMessage());
			e.printStackTrace();
			if (e instanceof SQLException) {
				throw (SQLException) e;
			} else
				throw new SQLException(e.getMessage());
		} finally {
			release();
		}
		return result;
	}

	void refresh(Object object) {

		if (bean == null) {
			bean = object;
			return;
		}

		if (!BeanUtils.describe(object).keySet().equals(
				BeanUtils.describe(bean).keySet())) {
			logger.warn("Attempt to refresh bean failed");
		} else {
			this.bean = object;
		}

	}

	private final void release() throws SQLException {

		logger.debug("AutoCommit = " + this.autoCommit);

		try {
			if (rs != null) {
				rs.close();
				rs = null;
			}
			if (stmt != null) {
				stmt.close();
				stmt = null;
			}
			if (ps != null) {
				ps.close();
				ps = null;
			}
		} catch (SQLException e) {
			logger.error(e.getMessage());
		} finally {

			logger.debug("conn=" + conn);

			if (conn != null && autoCommit) {
				conn.close();
				conn = null;
			}
		}
		logger.debug("conn=" + conn);
	}

	private final void removeNulls(Map map) {

		logger.debug("Keep nulls in SQL query :: " + this.keepNullsInQuery);

		List keysToRemove = new ArrayList();

		Iterator iter = map.keySet().iterator();
		while (iter.hasNext()) {

			String key = (String) iter.next();

			if (keepNullsInQuery) {

				// for empty Strings put null
				if ("".equals(map.get(key)) || map.get(key) == null) {
					// logger.debug(key + ":" + map.get(key));
					map.put(key, "null");
				}

			} else {

				if (map.get(key) == null) {
					// logger.debug(key + "=" + map.get(key));
					// remove these in a safe manner 9/12/06
					keysToRemove.add(key);
				}
			}
		}
		// remove keys from map
		iter = keysToRemove.iterator();
		while (iter.hasNext()) {
			map.remove(iter.next());
		}

	}

	public void rollback() {

		if (conn != null) {

			try {

				conn.rollback();
				this.autoCommit = true;
				release();

			} catch (SQLException e) {
				logger.error(e.getMessage());
				e.printStackTrace();
			} finally {
				this.autoCommit = true;
			}

		} else
			logger.warn("Connection already closed!");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see biz.topmind.jlynx.Relational#save()
	 */
	public int save() throws SQLException {

		logger.debug("Entering save()");

		int result = 0;

		try {
			result = update();
			if (result == 0)
				throw new SQLException();

		} catch (SQLException ex) {

			result = 0;
			logger.debug("update() failed... now trying to insert record");
			result = insert();
		}

		logger.debug("Exiting save()");
		return result;
	}

	public void saveNulls(boolean updateNulls) {
		this.keepNullsInQuery = updateNulls;
	}

	public final boolean select() throws SQLException {

		if (!validConfig) {
			logger.error("jLynx configuration invalid :: " + getEntity());
			throw new SQLException("jLynx configuration invalid");
		}

		if (!initialized)
			init();

		String sql = null;

		try {
			if (conn == null) {
				connect();
			}
			boolean result;
			sql = createSelectStmt();

			stmt = conn.createStatement();
			ResultSet resultset = stmt.executeQuery(sql);

			if ((result = resultset.next())) {
				if (bean instanceof Map) {
					setSingleRowValues(resultset);
					logger.debug("Map " + bean);
				} else
					this.bean = setValues(resultset, this.bean);
			}

			logger.debug("SELECT stmt: " + sql + "::" + result);

			return result;

		} catch (Exception e) {
			// logger.error(sql);
			logger.error(e.getMessage());
			e.printStackTrace();
			if (e instanceof SQLException) {
				throw (SQLException) e;
			} else
				throw new SQLException(e.getMessage());

		} finally {

			release();

		}
	}

	public Relational setAutoCommit(boolean b) {
		this.autoCommit = b;
		return this;
	}

	public Relational setConnection(Connection cn) throws SQLException {
		this.conn = cn;
		this.autoCommit = conn.getAutoCommit();
		if (getEntity() != null)
			init();

		return this;
	}

	public Relational setEntity(String entity) {

		this.entityName = entity;
		try {
			init();
		} catch (SQLException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		return this;
	}

	public Relational setObject(Object bean) {
		if (!(bean instanceof Map))
			this.bean = bean;
		else
			this.bean = bean;
		return this;
	}

	private void setParams(Object[] params) throws SQLException {
		if (params != null && params.length > 0) {
			for (int j = 0; j < params.length; j++) {
				Object o = params[j];
				if (o instanceof Integer)
					ps.setInt(j + 1, ((Integer) o).intValue());
				else
					ps.setObject(j + 1, o);
			}
		}
	}

	private void setSingleRowValues(ResultSet rs) throws Exception {
		if (bean != null && bean instanceof Map) {

			((Map) bean).clear();

			// assumes a single record
			for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
				((Map) bean).put(rs.getMetaData().getColumnName(i), rs
						.getObject(i));
			}
		}
	}

	public String toJSON() {
		return BeanUtils.toJson(bean);
	}

	public String toXml() {
		return toXml(null);
	}

	public String toXml(String element) {
		logger.debug("Entering method toXml");
		return BeanUtils.toXml(bean, element);
	}

	public final int update() throws SQLException {

		if (!validConfig)
			throw new SQLException("jLynx configuration invalid");

		if (!initialized)
			init();

		String sql = null;
		try {
			int result;
			sql = createUpdateStmt();

			if (conn == null) {
				connect();
			}
			stmt = conn.createStatement();

			logger.debug(sql);

			result = stmt.executeUpdate(sql);
			return result;

		} catch (Exception e) {
			logger.error(e.getMessage());
			if (e instanceof SQLException) {
				throw (SQLException) e;
			} else
				throw new SQLException(e.getMessage());
		} finally {
			release();
		}
	}

}// end class
